
#include <BFile>
#include <BYamlDocument>
#include "member_BYamlDocument.h"
#include "member_BYamlNode.h"

using namespace BWE;

#define member					(*(member_BYamlDocument*)_ptr)
#define member_allocate()		_ptr = new member_BYamlDocument(this)
#define member_release()		delete (member_BYamlDocument*)_ptr

BYamlDocument::BYamlDocument()
{
	member_allocate();
}
BYamlDocument::~BYamlDocument()
{
	member_release();
}

void BYamlDocument::setLevelIndent(int levelIndent)
{
	member.levelIndent = levelIndent;
}
int BYamlDocument::levelIndent() const
{
	return member.levelIndent;
}

BYamlNode* BYamlDocument::load(const BString& fileName)
{
	BFile file(fileName);
	if (file.open(IO_ReadText))
	{
		return read(file);
	}
	return 0;
}
bool BYamlDocument::save(const BString& fileName, const BYamlNode* node) const
{
	if (!node)
		return false;
	BFile file(fileName);
	if (file.open(IO_WriteText))
	{
		return write(file, node);
	}
	return false;
}
bool BYamlDocument::save(const BString& fileName) const
{
	if (member.node.empty() && member.topNodes.empty())
		return  false;
	BFile file(fileName);
	if (!file.open(IO_WriteText))
		return false;
	return write(file);
}

BYamlNode* BYamlDocument::read(const BString& yamlext)
{
	member.node = 0;
	member.topNodes.clear();
	BString line;
	int pos = 0;
	while (pos < yamlext.size())
	{
		pos = yamlext.line(line, pos);
		int indent = member.readLevel(line);
		{
			BYamlNode* node = member.node;
			while (node && yaml_member(node)->indent >= indent)
			{
				node = node->parent();
			}
			member.node = node;
		}
		line.trimmed();
		BYamlNode* node = new BYamlNode();
		yaml_member(node)->indent = indent;
		int comm_pos = line.find('#');
		if (comm_pos < line.size())
		{
			line.substr(yaml_member(node)->comment, comm_pos + 1);
			yaml_member(node)->comment.trimmed();
			line.truncate(0, comm_pos);
		}
		line.trimmed();
		int split = line.find(':');
		if (split < line.size())
		{
			BString name;
			line.substr(name, 0, split);
			line.remove(0, split + 1);
			node->setName(name);
			node->setText(line);
		}
		else
		{
			node->setName(line);
		}
		if (BYamlNode* parent = member.node)
			parent->addChild(node);
		else
			member.topNodes.append(node);
		member.node = node;
	}
	for (int i = 0; i < member.topNodes.size(); i++)
	{
		member.arrangeIndent(member.topNodes[i]);
	}
	member.node = member.topNodes.first();
	return member.node;
}
bool BYamlDocument::write(BString& yamlext, const BYamlNode* node) const
{
	if (node)
	{
		member.writeNode(node, yamlext, 0);
		return true;
	}
	return false;
}
bool BYamlDocument::write(BString& yamlext) const
{
	if (member.node.empty() && member.topNodes.empty())
		return false;
	if (member.topNodes.size())
	{
		for (int i = 0; i < member.topNodes.size(); i++)
		{
			member.writeNode(member.topNodes[i], yamlext, 0);
		}
		return true;
	}
	if (member.node)
	{
		member.writeNode(member.node, yamlext, 0);
		return true;
	}
	return false;
}

BYamlNode* BYamlDocument::read(const BStream& stream)
{
	member.node = 0;
	member.topNodes.clear();
	BString line;
	while (!stream.end())
	{
		if (!stream.readLine(line))
			break;
		int indent = member.readLevel(line);
		{
			BYamlNode* node = member.node;
			while (node && yaml_member(node)->indent >= indent)
			{
				node = node->parent();
			}
			member.node = node;
		}
		line.trimmed();
		BYamlNode* node = new BYamlNode();
		yaml_member(node)->indent = indent;
		int comm_pos = line.find('#');
		if (comm_pos < line.size())
		{
			line.substr(yaml_member(node)->comment, comm_pos + 1);
			yaml_member(node)->comment.trimmed();
			line.truncate(0, comm_pos);
		}
		line.trimmed();
		int split = line.find(':');
		if (split < line.size())
		{
			BString name;
			line.substr(name, 0, split);
			line.remove(0, split + 1);
			node->setName(name);
			node->setText(line);
		}
		else
		{
			node->setName(line);
		}
		if (BYamlNode* parent = member.node)
			parent->addChild(node);
		else
			member.topNodes.append(node);
		member.node = node;
	}
	for (int i = 0; i < member.topNodes.size(); i++)
	{
		member.arrangeIndent(member.topNodes[i]);
	}
	member.node = member.topNodes.first();
	return member.node;
}
bool BYamlDocument::write(BStream& stream, const BYamlNode* node) const
{
	if (!node)
		return false;
	member.writeNode(node, stream, 0);
	return true;
}
bool BYamlDocument::write(BStream& stream) const
{
	if (member.node.empty() && member.topNodes.empty())
		return false;
	if (member.topNodes.size())
	{
		for (int i = 0; i < member.topNodes.size(); i++)
		{
			member.writeNode(member.topNodes[i], stream, 0);
		}
		return true;
	}
	if (member.node)
	{
		member.writeNode(member.node, stream, 0);
		return true;
	}
	return false;
}

void BYamlDocument::setNode(BYamlNode* node)
{
	member.node = node;
}
BYamlNode* BYamlDocument::node()
{
	return member.node;
}

void BYamlDocument::addNode(BYamlNode* node)
{
	member.topNodes.append(node);
}
BYamlNode* BYamlDocument::addNode(const BString& name)
{
	BYamlNodeHolder node = new BYamlNode(name);
	addNode(node);
	return node;
}

bool BYamlDocument::removeNode(BYamlNode* node)
{
	return member.topNodes.remove(node);
}
bool BYamlDocument::removeNode(const BString& name)
{
	if (BYamlNode* node = this->node(name))
	{
		return member.topNodes.remove(node);
	}
	return false;
}
void BYamlDocument::clearNodes()
{
	member.topNodes.clear();
}

int BYamlDocument::nodeCount() const
{
	return member.topNodes.size();
}
BYamlNode* BYamlDocument::node(int index)
{
	return member.topNodes(index);
}
const BYamlNode* BYamlDocument::node(int index) const
{
	return member.topNodes(index);
}

BYamlNode* BYamlDocument::node(const char* name)
{
	if (!name)
		return 0;
	for (int i = 0; i < member.topNodes.size(); i++)
	{
		if (member.topNodes[i]->name() == name)
			return member.topNodes[i];
	}
	return 0;
}
const BYamlNode* BYamlDocument::node(const char* name) const
{
	if (!name)
		return 0;
	for (int i = 0; i < member.topNodes.size(); i++)
	{
		if (member.topNodes[i]->name() == name)
			return member.topNodes[i];
	}
	return 0;
}

BYamlNode* BYamlDocument::node(const BString& name)
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.topNodes.size(); i++)
	{
		if (member.topNodes[i]->name() == name)
			return member.topNodes[i];
	}
	return 0;
}
const BYamlNode* BYamlDocument::node(const BString& name) const
{
	if (name.empty())
		return 0;
	for (int i = 0; i < member.topNodes.size(); i++)
	{
		if (member.topNodes[i]->name() == name)
			return member.topNodes[i];
	}
	return 0;
}

BYamlNode* BYamlDocument::create(const BString& path)
{
	int end = path.find('/');
	if (end == path.size())
	{
		BYamlNode* node = this->node(path);
		if (node == 0)
			node = this->addNode(path);
		return node;
	}
	BString text;
	path.substr(text, 0, end);
	BYamlNode* node = this->node(text);
	if (node == 0)
	{
		node = this->addNode(text);
	}
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		BYamlNode* child = node->child(text);
		if (child == 0)
			child = node->addChild(text);
		node = child;
		pos = end + 1;
	}
	return node;
}

BYamlNode* BYamlDocument::locate(const BString& path)
{
	int end = path.find('/');
	if (end == path.size())
		return this->node(path);
	BString text;
	path.substr(text, 0, end);
	BYamlNode* node = this->node(text);
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		node = node->child(text);
		pos = end + 1;
	}
	return node;
}
const BYamlNode* BYamlDocument::locate(const BString& path) const
{
	int end = path.find('/');
	if (end == path.size())
		return this->node(path);
	BString text;
	path.substr(text, 0, end);
	const BYamlNode* node = this->node(text);
	int pos = end + 1;
	while (node && pos < path.size())
	{
		end = path.find('/', pos);
		path.substr(text, pos, end);
		node = node->child(text);
		pos = end + 1;
	}
	return node;
}

